"""
上传excel数据并解析到数据库
"""

import base64
import io
from typing import List, Callable, Literal
from flask import request
from dash import get_app, dcc, html, callback, Output, Input, State, MATCH,no_update,ctx
from ddbtools import BaseCRUD, get_table_columns, DBDf
import pandas as pd
from gypb.db import get_db
from gypb.component import DFTable
import feffery_antd_components as fac

app = get_app()


class DDBUploader:
    instance_store = {}  # 用于存储实例

    def __init__(
        self,
        name: str,
        db_path: str,
        table_name: str,
        key_cols: List[str],
        cache_dir=None,
        parser: Callable[[pd.DataFrame], pd.DataFrame] = None,
        header=0,
        display_mode:Literal["server-side", "client-side"] = "server-side",
    ):
        self.name = name
        self.db_path = db_path
        self.table_name = table_name
        self.key_cols = key_cols
        self.cache_dir = cache_dir
        self.parser = parser
        self.header = header
        self.display_mode = display_mode

        self.upload_id = {"type": "ddbupload-upload", "index":self.name}
        self.table_upload_id = {"type": "ddbupload-table", "index":self.name}
        self.button_upload_id = {"type": "ddbupload-button", "index":self.name}
        self.notification_id = {"type": "ddbupload-notification", "index":self.name}

        DDBUploader.instance_store[name] = self

    @property
    def layout(self):
        components = []
        components.append(self.get_uploader())
        components.append(html.Div(id=self.table_upload_id))
        components.append(
            fac.AntdButton("上传数据库", id=self.button_upload_id, type="primary")
        )
        components.append(html.Div(id=self.notification_id))
        return html.Div(components)

    def get_uploader(self):
        return dcc.Upload(
            id=self.upload_id,
            children=html.Div(["点击或拖拽文件至此处进行上传"]),
            multiple=True,
            style={
            'width': '100%',
            'height': '120px',
            'lineHeight': '120px',
            'borderWidth': '1px',
            'borderStyle': 'dashed',
            'borderRadius': '5px',
            'textAlign': 'center',
            'margin': '10px'
        },
        )

    def get_df(self, content, filename):
        # 检查文件内容
        if content is not None:
            # 数据格式为 base64
            content_type, content_string = content.split(",")
            decoded = base64.b64decode(content_string)

            if "csv" in filename:
                # Assume that the user uploaded a CSV file
                df = pd.read_csv(io.StringIO(decoded.decode("utf-8")))
            elif "xls" in filename:
                # Assume that the user uploaded an excel file
                df = pd.read_excel(io.BytesIO(decoded), header=self.header)

            return df

    def df_parser(self, df: pd.DataFrame):
        if self.parser is not None:
            df = self.parser(df)
            self._cached_df = df
        return df

    def display_parsed_df(self, df: pd.DataFrame):
        return DFTable(
            df,
            self.db_path + "-" + self.table_name,
            mode=self.display_mode,
        ).layout

    def upserter(self, data: pd.DataFrame):
        crud = BaseCRUD(self.db_path, self.table_name)
        crud.key_cols = self.key_cols
        with get_db() as session:
            data = DBDf(
                session=session,
                db_path=self.db_path,
                table_name=self.table_name,
                data=data,
            )
            crud.upsert(session, data)


@callback(
    Output({"type": "ddbupload-table", "index": MATCH}, "children",allow_duplicate=True),
    Input({"type": "ddbupload-upload", "index": MATCH}, "contents"),
    State({"type": "ddbupload-upload", "index": MATCH}, "filename"),
    prevent_initial_call=True,
)
def parse_display_contents(list_of_contents, list_of_names):
    if list_of_contents is not None:
        ddbuploader_instance: DDBUploader = DDBUploader.instance_store[ctx.triggered_id["index"]]
        raw_df = pd.concat(
            [
                ddbuploader_instance.get_df(content, filename)
                for content, filename in zip(list_of_contents, list_of_names)
            ]
        ).drop_duplicates()
        df_parsed = ddbuploader_instance.df_parser(raw_df)
        return ddbuploader_instance.display_parsed_df(df_parsed.reset_index())
        
@callback(
    Output({"type": "ddbupload-notification", "index": MATCH}, "children"),
    Output({"type": "ddbupload-table", "index": MATCH}, "children",allow_duplicate=True),
    Input({"type": "ddbupload-button", "index": MATCH}, "nClicks"),
    prevent_initial_call=True,
)
def upsert_data(nClicks):
    if nClicks:
        ddbuploader_instance: DDBUploader = DDBUploader.instance_store[ctx.triggered_id["index"]]
        if hasattr(ddbuploader_instance, "_cached_df"):
            ddbuploader_instance.upserter(ddbuploader_instance._cached_df)
            del ddbuploader_instance._cached_df
            return (fac.AntdNotification(
                message="上传成功",
                description=f"成功上传数据",
                type="success",
            ), None)
        else:
            return (fac.AntdNotification(
                message="上传失败",
                description="请先上传数据",
                type="error",
            ), no_update)
